package com.draw.cli.gui.panel.draw;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.net.MalformedURLException;
import java.util.Date;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.xml.ws.WebServiceException;

import com.draw.cli.gui.Loading;
import com.draw.cli.gui.Window;
import com.draw.cli.gui.panel.Refreshable;
import com.draw.cli.webclient.DrawingClient;
import com.draw.core.Color;
import com.draw.core.Drawing;
import com.draw.core.Line;
import com.draw.core.Point;
import com.draw.core.User;
import com.draw.core.Vector;
import com.draw.webinterface.DrawingInterface;

public class DrawPanel extends JPanel implements MouseListener,
		MouseMotionListener, ActionListener, Runnable, Refreshable {
	private static final long serialVersionUID = -6365770826114660479L;
	private static final String DRAWING_SENT = "Votre dessin a bien été envoyé.";
	private static final String DRAWING_SENDING_ERROR = "Erreur durant l'envoi du dessin.";

	private static final Dimension size = new Dimension(400, 500);

	private static DrawPanel instance = new DrawPanel();

	public static DrawPanel getInstance() {
		return instance;
	}

	private ToolboxPanel toolbox;
	private ChooseWordPanel chooser;
	private JPanel pane;
	private JButton send;

	private Drawing drawing;
	private Line line;
	private Vector vector;

	private boolean pressed;

	private BufferedImage buffer;

	public DrawPanel() {
		this.toolbox = null;
		this.setLayout(new BorderLayout());
		this.pane = new JPanel() {
			private static final long serialVersionUID = -512963097825615797L;

			public void paint(Graphics g) {
				super.paint(g);
				this.setBackground(java.awt.Color.WHITE);
				if (buffer != null) {
					g.drawImage(buffer, 0, 0, this);
				}
			}
		};
		this.pane.setBorder(BorderFactory.createLoweredBevelBorder());
		this.pane.setPreferredSize(size);

		this.pane.addMouseListener(this);
		this.pane.addMouseMotionListener(this);
		this.pane.setBackground(java.awt.Color.WHITE);

		JPanel pa = new JPanel(new FlowLayout());
		pa.add(this.pane);
		this.add(pa, BorderLayout.CENTER);
		/***/
		this.drawing = null;
		this.line = null;
		this.vector = null;
		this.pressed = false;
		/***/

		this.chooser = new ChooseWordPanel();
		this.send = new JButton("Envoyer");
		this.send.addActionListener(this);
		this.chooser.add(this.send);
		this.add(this.chooser, BorderLayout.EAST);
	}

	public void initialize(User source, User destination)
			throws MalformedURLException, WebServiceException {
		if (this.toolbox != null) {
			this.remove(this.toolbox);
		}
		this.toolbox = new ToolboxPanel();
		this.add(this.toolbox, BorderLayout.WEST);
		this.buffer = new BufferedImage(size.width, size.height,
				BufferedImage.TYPE_4BYTE_ABGR);
		this.drawing = new Drawing();
		this.drawing.setSource(source);
		this.drawing.setDestination(destination);
		this.chooser.initWords();
		this.send.setEnabled(false);
	}

	public void mouseClicked(MouseEvent arg0) {

	}

	public void mouseEntered(MouseEvent arg0) {
		if (this.pressed) {
			this.vector = new Vector();
			this.vector.setSource(this.getPoint(arg0));
			// this.line.addVector(this.vector);
		}
	}

	public void mouseExited(MouseEvent arg0) {
		if (this.pressed) {
			this.vector.setDestination(this.getPoint(arg0));
			this.line.addVector(this.vector);
			this.vector = null;
		}
	}

	public void mousePressed(MouseEvent arg0) {
		this.send.setEnabled(false);
		this.pressed = true;
		this.line = new Line();
		// this.line.setDrawing(this.drawing);
		this.line.setColor(this.toolbox.getColor());
		this.line.setThickness(this.toolbox.getThickness());

		this.vector = new Vector();
		this.vector.setSource(this.getPoint(arg0));
	}

	public void mouseReleased(MouseEvent arg0) {
		this.pressed = false;
		Point p = this.getPoint(arg0);
		if (p.getX() >= 0 && p.getY() >= 0 && p.getX() < size.width
				&& p.getY() < size.height) {
			this.vector.setDestination(p);
			this.line.addVector(this.vector);
		}
		this.vector = null;
		this.drawing.addLine(this.line);
		this.line = null;
		this.send.setEnabled(true);
	}

	public void mouseDragged(MouseEvent arg0) {
		Point p = this.getPoint(arg0);
		if (p.getX() >= 0 && p.getY() >= 0 && p.getX() < size.width
				&& p.getY() < size.height) {
			this.vector.setDestination(p);
			this.line.addVector(this.vector);
			this.drawVector(this.line, this.vector);

			this.vector = new Vector();
			this.vector.setSource(p);
		}
	}

	public Point getPoint(MouseEvent ev) {
		int thickness = this.toolbox.getThickness();
		Point p = new Point();
		int x = ev.getX() - (thickness/2);
		int y = ev.getY() - (thickness/2);
		p.setTimestamp((int) new Date().getTime());
		p.setX(x);
		p.setY(y);
		this.drawPoint(x, y);
		return p;
	}

	public void drawPoint(int x, int y) {
		if (this.line != null) {
			int thickness = this.line.getThickness();
			Color color = this.line.getColor();
			this.drawPoint(x, y, color, thickness);
		}
	}

	public void drawPoint(int x, int y, Color color, int thickness) {
		if (x >= 0 && y >= 0 && x < size.width && y < size.height) {
			Graphics g1 = this.buffer.getGraphics();
			Graphics g2 = this.pane.getGraphics();
			g1.setColor(line.getColor().getColor());
			g2.setColor(line.getColor().getColor());
			g1.fillOval(x, y, line.getThickness(), line.getThickness());
			g2.fillOval(x, y, line.getThickness(), line.getThickness());
			g1.dispose();
			g2.dispose();
		}
	}

	public void drawLine(Line line) {
		for (Vector vector : line.getVectors()) {
			this.drawVector(line, vector);
		}
	}

	public void drawVector(Line line, Vector vector) {
		Color color = line.getColor();
		int thickness = line.getThickness();
		int x_s = vector.getSource().getX();
		int y_s = vector.getSource().getY();
		int x_d = vector.getDestination().getX();
		int y_d = vector.getDestination().getY();
		if (x_s == x_d) {
			for (int i = y_s; i <= y_d; i++) {
				this.drawPoint(x_s, i, color, thickness);
			}
			for (int i = y_d; i <= y_s; i++) {
				this.drawPoint(x_s, i, color, thickness);
			}
		} else if (y_s == y_d) {
			for (int i = x_s; i <= x_d; i++) {
				this.drawPoint(i, y_s, color, thickness);
			}
			for (int i = x_d; i <= x_s; i++) {
				this.drawPoint(i, y_s, color, thickness);
			}
		} else {
			double a = ((double) (y_d - y_s)) / ((double) (x_d - x_s));
			double b = y_s - a * x_s;
			for (int i = x_s; i <= x_d; i++) {
				this.drawPoint(i, (int) (a * i + b), color, thickness);
			}
			for (int i = x_d; i <= x_s; i++) {
				this.drawPoint(i, (int) (a * i + b), color, thickness);
			}
			for (int i = y_s; i <= y_d; i++) {
				this.drawPoint((int) ((i - b) / a), i, color, thickness);
			}
			for (int i = y_d; i <= y_s; i++) {
				this.drawPoint((int) ((i - b) / a), i, color, thickness);
			}
		}
	}

	public void mouseMoved(MouseEvent arg0) {

	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		new Thread(this).start();
	}

	@Override
	public void run() {
		this.drawing.setWord(this.chooser.getSelectedWord());
		this.drawing.generateLetters();
		try {
			Loading.start();
			DrawingInterface di = DrawingClient.getInstance();
			this.drawing.unpopulate();
			di.addDraw(this.drawing);
			Loading.stop();

			JOptionPane.showMessageDialog(this, DRAWING_SENT,
					"Envoi du dessin", JOptionPane.INFORMATION_MESSAGE);
		} catch (MalformedURLException | WebServiceException e) {
			// e.printStackTrace();
			JOptionPane.showMessageDialog(this, DRAWING_SENDING_ERROR,
					"Envoi du dessin", JOptionPane.ERROR_MESSAGE);
		}
		Loading.start();
		Window.getInstance().setHome();
		Loading.stop();
	}

	@Override
	public void refresh() {
		// TODO Auto-generated method stub
		
	}
}
